﻿using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
namespace RWLib
{
    /// <summary>
    /// Literał 
    /// Przykład:
    /// 1. p
    /// 2. ~p
    /// </summary>
    public class Fluent : IEquatable<Fluent>
    {
        public String Name;
        public bool? _Value;
        public bool Negation=false;
        public bool Changed = false;

        public Fluent(String name, bool? value = null)
        {
            Name = name;
            Value = value;
            Negation = false;
        }

        public Fluent(String name, bool value, bool? negation=null)
        {
            Name = name;
            Value = value;
            if (negation != null)
            {
                Negation = negation.Value;
            }
        }

        public bool Conflicts(Fluent f)
        {
            if (Value != null && f.Value != null)
                return !((Negation ? !Value.Value : Value.Value).Equals(f.Negation ? !f.Value.Value : f.Value.Value));
            return false;
        }

        public bool Eval(Fluent f)
        {

            //Value = f.Value;
            if (f.Value == null)
            {
                return false;
            }
            Value = f.Value;
            return Negation ? !f.Value.Value : f.Value.Value;
        }

        public bool? Value {
            get { return _Value; }
            set { _Value = value; }
        }

        public bool Eval() {
            return Negation ? !Value.Value : Value.Value;
        }

        public object Clone()
        {
            Fluent ff = new Fluent(Name, Value);
            ff.Negation=Negation;
            ff.Changed = Changed;
            return ff;
        }

        public override int GetHashCode()
        {
            return base.GetHashCode();
        }

        public bool Equals(Fluent other)
        {
            return false;
        }

        //public override bool Equals(Object obj)
        //{
        //    Fluent personObj = obj as Fluent;
        //    if (personObj == null)
        //    {
        //        return false;
        //    }
        //    else
        //    {
        //        return personObj.Name.Equals(this.Name) && personObj.Value.Equals(this.Value)
        //            && personObj.Negation.Equals(this.Negation);
        //    }
        //}



        public override string ToString()
        {
            
            var sb2 = new StringBuilder();
            if (Negation) {
                sb2.Append("~");
            }

            return sb2.Append(Name).ToString();
        }

        static void Main(String [] args) {

            //var text = "initially not a and not b and not c\n" +
            //           "(a or not a) triggers A\n" +
            //           "A invokes A after 3\n" +
            //           "A causes b\n" +
            //           "A causes not b";
                       
            //var parser = new Parser();
            //parser.Parse(text);
            //SortedDictionary<int, List<string>> scenario = new SortedDictionary<int, List<string>>();
            //scenario.Add(1, new List<string> { "A" });
            //Tree tree = parser.BuildTree(scenario);

            //Console.WriteLine(tree);
            //Example1();

            var text = "initially not dead and loaded\n" +
                       "Load releases loaded\n" +
                       "Shoot causes dead and not dead";
            var parser = new Parser();
            parser.Parse(text);
            SortedDictionary<int, List<string>> scenario = new SortedDictionary<int, List<string>>();
            scenario.Add(1, new List<string> { "Shoot" });
            Tree tree = parser.BuildTree(scenario);
            Console.WriteLine(tree);

            //Formula adv = new Formula(new List<Clause>() {
            //    new Clause(new List<Fluent>(){new Fluent("dead")}),
            //    new Clause(new List<Fluent>(){new Fluent("loaded",false,true)})
            //});
            //ReleaseAction a1 = new ReleaseAction("Load", new Formula(), new Fluent("loaded", false));
            //FluentEffectAction a2 = new FluentEffectAction("Shoot", new Formula(new List<Clause>() {new Clause(new List<Fluent>(){new Fluent("loaded",false)})}),
            //    adv);
            //Console.WriteLine(a1.Conflict(a2, new List<Fluent>(){new Fluent("loaded")}));
            //List<Fluent> fluents = new List<Fluent>() { new Fluent("a", false, true), new Fluent("b", false, false), new Fluent("d", false, false)
            //, new Fluent("e", false, false), new Fluent("e", false, false)};
            //Formula f = new Formula(new List<Clause>() { 
            //    new Clause(new List<Fluent>() { fluents[0],fluents[1] }),
            //    new Clause(new List<Fluent>() { fluents[2] })
            //});
            //List<List<Fluent>> perm = f.Fluents.Permutates();
            //perm.ForEach(l =>
            //{
            //    System.Console.Write(f.ToString());
            //    System.Console.Write(" - ");
            //    System.Console.Write(String.Concat(l.Skip(1).Select(o => o.Eval() ? o + ":1," : o + ":0,")));
            //    System.Console.Write(" - ");
            //    System.Console.Write(String.Concat(f.Eval(l)));
            //    System.Console.WriteLine();
            //});
        }

        private static void Example1()
        {
            var ftrue = new Formula();
            var shootR = new Formula(new List<Clause>() { new Clause(new List<Fluent>(){new Fluent("hidden",false,true)}),
                new Clause(new List<Fluent>(){new Fluent("loadedR",false,false)})
                ,new Clause(new List<Fluent>(){new Fluent("alive",false,false)})
            });
            var shootL = new Formula(new List<Clause>() { 
             new Clause(new List<Fluent>(){new Fluent("hidden",false,true)})
            ,new Clause(new List<Fluent>(){new Fluent("loadedL",false,false)})
            ,new Clause(new List<Fluent>(){new Fluent("alive",false,false)})});

            //var Load_L = new ReleaseAction("Load_L", ftrue, new Formula(new List<Clause>() { new Clause(new List<Fluent>() { new Fluent("loadedL",false) }) }));
            var Load_L = new FluentEffectStatement("Load_L", ftrue, new Formula(new List<Clause>() { new Clause(new List<Fluent>() { new Fluent("loadedL", false) }) }));
            var Load_L2 = new FluentEffectStatement("Load_L1", ftrue, new Formula(new List<Clause>() { new Clause(new List<Fluent>() { new Fluent("loadedR", false) }) }));
            var Load_R = new FluentEffectStatement("Load_R", ftrue, new Formula(new List<Clause>() { new Clause(new List<Fluent>() { new Fluent("loadedR", false) }) }));
            var Shoot_Fred_right = new FluentEffectStatement("Shoot_Fred_right", shootR,
                new Formula(new List<Clause>() { new Clause(new List<Fluent>() { new Fluent("alive", false, true) }) }));
            var Shoot_Fred_left = new ReleaseStatement("Shoot_Fred_left", shootL, new Fluent("alive", false, true));
            var Sing = new FluentEffectStatement("Sing", new Formula(new List<Clause>() { new Clause(new List<Fluent>() { new Fluent("hidden", false, true) }) }),
                new Formula(new List<Clause>() { new Clause(new List<Fluent>() { new Fluent("hidden", false, false) }) }));
            var Unhide = new FluentEffectStatement("Unhide", ftrue, new Formula(new List<Clause>() { new Clause(new List<Fluent>() { new Fluent("hidden", false, true) }) }));
            var Wait = new ActionEffectStatement("Wait", ftrue, Unhide, 3);

            var tcond = new Formula(new List<Clause>() { new Clause(new List<Fluent>() { new Fluent("hidden", false, false) }) });
            var trigger = new TriggerStatement("trigger_wait", tcond, Wait);

            var state = new List<Fluent>() { new Fluent("loadedL", false), new Fluent("hidden", false) };

            SortedDictionary<int, List<StatementBase>> actions = new SortedDictionary<int, List<StatementBase>>();
            actions.Add(1, new List<StatementBase>() { Load_R });
            actions.Add(2, new List<StatementBase>() { Sing });
            actions.Add(8, new List<StatementBase>() { Shoot_Fred_right });

            SortedDictionary<int, List<string>> actionsNames = new SortedDictionary<int, List<string>>();
            actionsNames.Add(1, new List<string>() { Load_R.Name });
            actionsNames.Add(2, new List<string>() { Sing.Name });
            actionsNames.Add(8, new List<string>() { Shoot_Fred_right.Name });

            //actionsNames.Add(1, new List<string> { "Load_R" });
            //actionsNames.Add(3, new List<string> { "Load_L" });
            //actionsNames.Add(5, new List<string> { "Shoot_Fred_left" });

            List<Fluent> initFluents = new List<Fluent>() { new Fluent("loadedR", false), new Fluent("loadedL", false), new Fluent("alive", true), new Fluent("hidden", false) };
            Scenario sc = new Scenario(actionsNames, initFluents);

            ActionDomain domain = new ActionDomain();
            domain.Triggers = new List<TriggerStatement>() { trigger };
            domain.Actions = new List<StatementBase>() { Load_L, Load_L2, Load_R, Shoot_Fred_left, Shoot_Fred_right, Sing, Unhide, Wait };

            sc._initialFormula = new Formula(new List<Clause>()
            {
                new Clause(new List<Fluent>(){new Fluent("loadedR", false,true)}),
                new Clause(new List<Fluent>(){new Fluent("loadedL", false,true)}),
                new Clause(new List<Fluent>(){new Fluent("hidden", false,true)}),
                new Clause(new List<Fluent>(){new Fluent("alive", true,false),new Fluent("alive", true,true)})
            });


            //var test = new List<Fluent>() {
            //    new Fluent("loadedR",false),
            //    new Fluent("loadedL",false),
            //    new Fluent("hidden",false),
            //    new Fluent("alive",false),
            //    new Fluent("alive",false)
            //};
            //Console.WriteLine(String.Join("\n",test.Distinct().Select(f=>f.ToString()).ToArray()));
            //Console.WriteLine();
            //Console.WriteLine(String.Join("\n", test.Select(f => f.ToString()).ToArray()));
            //Console.WriteLine();
            Tree tree = Tree.build(sc, domain,true);
            Console.WriteLine(tree);

            //Query query1 = new AlwaysReachableQuery(tree, new Formula(new List<Clause>() { new Clause(new List<Fluent>() { new Fluent("alive", false, true) }) }));
            //Console.WriteLine(query1.Run());

            //Query query2 = new SometimesReachableQuery(tree, new Formula(new List<Clause>() { new Clause(new List<Fluent>() { new Fluent("alive", false, true) }) }));
            //Console.WriteLine(query2.Run());
            Console.WriteLine(new ActionExecutabilityQuery(tree, Shoot_Fred_left.Name).Run());
            Console.WriteLine(new ActionExecutabilityQuery(tree, Shoot_Fred_right.Name).Run());
        }

        private static void Example2() {            
            var WORKING="working";
            var THIRSTY="thirsty";
            var HUNGRY = "hungry";
            var DRESSED="dressed";
            var BOILED = "boiled";
            var CLEAN = "clean";

            var ftrue = new Formula();

            var go = new Formula(new List<Clause>(){new Clause(new List<Fluent>(){new Fluent(THIRSTY,false,true)}),
            new Clause(new List<Fluent>(){new Fluent(HUNGRY,false,true)}),
            new Clause(new List<Fluent>(){new Fluent(CLEAN,false,false)}),
            new Clause(new List<Fluent>(){new Fluent(DRESSED,false,false)})
            });

            var GoToWork = new FluentEffectStatement("gotowork", go,new Formula( new List<Clause>(){ new Clause(new List<Fluent>(){ new Fluent(WORKING, false, false)})}));

            var Eat = new FluentEffectStatement("eat", ftrue, new Formula(new List<Clause>() { new Clause(new List<Fluent>() { new Fluent(HUNGRY, false, true) }) }));

            var Boil = new FluentEffectStatement("boil", ftrue, new Formula(new List<Clause>() { new Clause(new List<Fluent>() { new Fluent(BOILED, false, false) }) }));

            var TurnKettleOn = new ActionEffectStatement("TurnKettleOn", ftrue, Boil, 4);

            var DrinkTea = new FluentEffectStatement("drinktea", ftrue, new Formula(new List<Clause>() { new Clause(new List<Fluent>() { new Fluent(THIRSTY, false, true) }) }));

            DrinkTea.ImpossibleCondition = new Formula(new List<Clause>() { new Clause(new List<Fluent>(){new Fluent(BOILED,false,true)})});

            var showerCond=new Formula(new List<Clause>(){new Clause(new List<Fluent>(){new Fluent(HUNGRY,false,true)})});
            var TakeShower = new FluentEffectStatement("takeShower", showerCond, new Formula(new List<Clause>() { 
                new Clause(new List<Fluent>() { new Fluent(DRESSED, false, true)}),
                new Clause(new List<Fluent>() { new Fluent(CLEAN, false, false)})
            }));

            var DressUp = new FluentEffectStatement("dressUp", ftrue, new Formula(new List<Clause>() { new Clause(new List<Fluent>() { new Fluent(DRESSED, false, false) }) }));

            SortedDictionary<int, List<StatementBase>> actions = new SortedDictionary<int, List<StatementBase>>();
            actions.Add(1, new List<StatementBase>() { Eat,TurnKettleOn });
            actions.Add(2, new List<StatementBase>() { TakeShower });
            actions.Add(3, new List<StatementBase>() { DressUp });
            actions.Add(5, new List<StatementBase>() { DrinkTea });
            actions.Add(7, new List<StatementBase>() { GoToWork });

            List<Fluent> initFluents = new List<Fluent>() { new Fluent(WORKING, false), new Fluent(THIRSTY, true),
                new Fluent(HUNGRY, true), new Fluent(CLEAN, false), new Fluent(DRESSED, false), new Fluent(BOILED, false) };
            Scenario sc = new Scenario(actions, initFluents);

            ActionDomain domain = new ActionDomain();
            domain.Triggers = new List<TriggerStatement>() {};
            domain.Actions = new List<StatementBase>() { GoToWork, Eat, Boil, TurnKettleOn, DrinkTea, TakeShower, DressUp };

            Tree tree = Tree.build(sc, domain);
            Console.WriteLine(tree);
            
        }

        private static void Example3() {
            var blockedSellB = "blockedSellB";
            var blockedSellA = "blockedSellA";
            var blockedBuyA = "blockedBuyA";
            var blockedBuyB = "blockedBuyB";

            var ftrue = new Formula();

            var SellA = new FluentEffectStatement("SellA", ftrue, new Formula(new List<Clause>() { new Clause(new List<Fluent>(){new Fluent(blockedBuyB,false,false)})}));
            SellA.ImpossibleCondition = new Formula(new List<Clause>() { new Clause(new List<Fluent>() { new Fluent(blockedSellA, false, false) }) });
            var UnblockBuyB = new FluentEffectStatement("UnblockBuyB", ftrue, new Formula(new List<Clause>() { new Clause(new List<Fluent>() { new Fluent(blockedBuyB, false, true) }) }));
            var blockedBuyBTrigger = new TriggerStatement("blockedBuyBTrigger", ftrue, UnblockBuyB);

            var SellB = new FluentEffectStatement("SellB", ftrue, new Formula(new List<Clause>() { new Clause(new List<Fluent>() { new Fluent(blockedBuyA, false, false) }) }));
            SellB.ImpossibleCondition = new Formula(new List<Clause>() { new Clause(new List<Fluent>() { new Fluent(blockedSellB, false, false) }) });
            var UnblockBuyA = new FluentEffectStatement("SellA", ftrue, new Formula(new List<Clause>() { new Clause(new List<Fluent>() { new Fluent(blockedBuyA, false, true) }) }));
            var blockedBuyATrigger = new TriggerStatement("blockedBuyATrigger", ftrue, UnblockBuyA);

            var BuyA = new FluentEffectStatement("BuyA", ftrue, new Formula(new List<Clause>() { new Clause(new List<Fluent>() { new Fluent(blockedSellB, false, false) }) }));
            BuyA.ImpossibleCondition = new Formula(new List<Clause>() { new Clause(new List<Fluent>() { new Fluent(blockedBuyA, false, false) }) });
            var UnblockSellB = new FluentEffectStatement("UnblockSellB", ftrue, new Formula(new List<Clause>() { new Clause(new List<Fluent>() { new Fluent(blockedSellB, false, true) }) }));
            var blockedSellBTrigger = new TriggerStatement("blockedSellBTrigger", ftrue, UnblockSellB);

            var BuyB = new FluentEffectStatement("BuyB", ftrue, new Formula(new List<Clause>() { new Clause(new List<Fluent>() { new Fluent(blockedSellA, false, false) }) }));
            BuyB.ImpossibleCondition = new Formula(new List<Clause>() {new Clause(new List<Fluent>(){new Fluent(blockedBuyB,false,false)}) });
            var UnblockSellA = new FluentEffectStatement("UnblockSellA", ftrue, new Formula(new List<Clause>() { new Clause(new List<Fluent>() { new Fluent(blockedSellA, false, true) }) }));
            var blockedSellATrigger = new TriggerStatement("blockedSellATrigger", ftrue, UnblockSellA);

            SortedDictionary<int, List<StatementBase>> actions = new SortedDictionary<int, List<StatementBase>>();
            actions.Add(1, new List<StatementBase>() { SellA});
            actions.Add(2, new List<StatementBase>() { BuyA});
            actions.Add(4, new List<StatementBase>() { BuyA,SellB});

            List<Fluent> initFluents = new List<Fluent>() { new Fluent(blockedSellB, false), new Fluent(blockedSellA, false), new Fluent(blockedBuyA, false)
                , new Fluent(blockedBuyB, false) };
            Scenario sc = new Scenario(actions, initFluents);

            ActionDomain domain = new ActionDomain();
            domain.Triggers = new List<TriggerStatement>();
            domain.Actions = new List<StatementBase>() { SellA,SellA,UnblockBuyA,UnblockBuyB,blockedBuyATrigger,blockedBuyBTrigger,blockedSellATrigger,blockedSellBTrigger,
            UnblockSellA,UnblockSellB};

            Tree tree = Tree.build(sc, domain);
            Console.WriteLine(tree);
        }
       
    }
}